﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using Infrastructure.Domain.SeedWork;
using Infrastructure.Domain.SeedWork.Core;
using Infrastructure.Domain.SeedWork.Core.Specification;
using Infrastructure.Repository.EntityFramework.Extension;

namespace Infrastructure.Repository.EntityFramework.Repository
{
    public class Repository<TEntity> : IRepository<TEntity>, IRepositoryAdapter<TEntity> where TEntity : Entity
    {
        #region Members

        protected readonly IQueryableUnitOfWork _unitOfWork;

        #endregion

        #region Constructor

        /// <summary>
        ///     Create a new instance of repository
        /// </summary>
        /// <param name="unitOfWork">Associated Unit Of Work</param>
        public Repository(IQueryableUnitOfWork unitOfWork)
        {
            if (unitOfWork == null)
                throw new ArgumentNullException("unitOfWork");

            _unitOfWork = unitOfWork;
        }

        #endregion

        #region IRepository Members

        public IUnitOfWork UnitOfWork
        {
            get { return _unitOfWork; }
        }

        public void Modify(TEntity item)
        {
            _unitOfWork.SetModified(item);
        }

        public virtual TEntity Get(long id)
        {
            return id != 0 ? getSet().Find(id) : null;
        }

        public virtual void Add(TEntity item)
        {
            //新增如果没有赋值的话则主动赋值，有赋值的 话则使用现有值
            //此判断主要处理跨页面逻辑的问题,比如附件
            if (item.Id == 0)
            {
                item.Id = IdentityGenerator.NewSequentialDomainId();
            }


            getSet().Add(item); // add new item in this set
        }

        public virtual void Remove(TEntity item)
        {
            //attach item if not exist
            _unitOfWork.Attach(item);
            //set as "removed"
            getSet().Remove(item);
        }

        public void Remove(long id)
        {
            TEntity item = Get(id);
            Remove(item);
        }


        public TEntity GetFirst(Expression<Func<TEntity, bool>> filter)
        {
            return getSet().OrderBy(item => item.Id).FirstOrDefault(filter);
        }

        public TEntity GetLast(Expression<Func<TEntity, bool>> filter)
        {
            return getSet().SingleOrDefault(filter);
        }

        public TEntity Single(Expression<Func<TEntity, bool>> filter)
        {
            return getSet().OrderBy(item => item.Id).FirstOrDefault(filter);
        }

        public virtual IEnumerable<TEntity> GetAll()
        {
            return getSet();
        }

        public IEnumerable<TEntity> GetPaged<TProperty>(int pageIndex, int pageCount,
            Expression<Func<TEntity, TProperty>> orderByExpression, bool @ascending,
            Expression<Func<TEntity, bool>> filter = null)
        {
            return getPagedByExpression(pageIndex, pageCount, orderByExpression, @ascending, filter);
        }

        public virtual IEnumerable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter)
        {
            return getSet().Where(filter);
        }

        public IEnumerable<TEntity> GetFiltered(ISpecification<TEntity> specification)
        {
            return GetFiltered(specification.SatisfiedBy());
        }

        public IEnumerable<TEntity> GetPaged<TProperty>(int pageIndex, int pageCount,
            Expression<Func<TEntity, TProperty>> orderByExpression, bool @ascending,
            ISpecification<TEntity> specification)
        {
            return getPagedByExpression(pageIndex, pageCount, orderByExpression, @ascending, specification.SatisfiedBy());
        }

        public virtual TEntity GetFirst(ISpecification<TEntity> specification)
        {
            return getSet().OrderBy(item => item.Id).FirstOrDefault(specification.SatisfiedBy());
        }

        public TEntity Single(ISpecification<TEntity> specification)
        {
            return getSet().SingleOrDefault(specification.SatisfiedBy());
        }

        public TEntity GetLast(ISpecification<TEntity> specification)
        {
            return getSet().OrderByDescending(item => item.Id).FirstOrDefault(specification.SatisfiedBy());
        }

        public virtual void Merge(TEntity persisted, TEntity current)
        {
            _unitOfWork.ApplyCurrentValues(persisted, current);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (_unitOfWork != null)
                _unitOfWork.Dispose();
        }

        #endregion

        #region Private Methods

        private IDbSet<TEntity> _entities;

        protected IDbSet<TEntity> getSet()
        {
            return _entities ?? (_entities = _unitOfWork.CreateSet<TEntity>());
        }

        private IEnumerable<TEntity> getPagedByExpression<TProperty>(int pageIndex, int pageCount,
            Expression<Func<TEntity, TProperty>> orderByExpression, bool @ascending,
            Expression<Func<TEntity, bool>> filter = null)
        {
            if (pageIndex == 0)
            {
                pageIndex = 1;
            }
            IQueryable<TEntity> set = filter == null ? getSet() : getSet().Where(filter);

            int totel = set.Count();

            IEnumerable<TEntity> items;
            if (ascending)
            {
                items = set.OrderBy(orderByExpression)
                    .Skip(pageCount*(pageIndex - 1))
                    .Take(pageCount);
            }
            else
            {
                items = set.OrderByDescending(orderByExpression)
                    .Skip(pageCount*(pageIndex - 1))
                    .Take(pageCount);
            }
            return items;
        }

        #endregion
    }
}